<?php
class Executor {
	
	private $schedulerService;
	private $taskService;
	
	private $hostname;
	private $executor;
	
	public function __construct() {
		$this->hostname = php_uname('n');
		$this->schedulerService = SchedulerService::getInstance();
		$this->taskService = TaskService::getInstance();
		
		// make lock & runtime path
		if(!file_exists(LOCK_PATH)) {
			mkdir(LOCK_PATH);
		}
		if(!file_exists(RUNTIME_PATH)) {
			mkdir(RUNTIME_PATH);
		}
	}
	/**
	 * Check whether there is a executor is online
	 */
	public function isOnline() {
		
		$this->executor = $this->schedulerService->getExecutorMachineByName($this->hostname);
		
		if(!isset($this->executor)) {
			Yii::log("executor[{$this->hostname}] is not found, exit", CLogger::LEVEL_ERROR, EXECUTOR_LOG);
			return false;
		}
		
		if($this->executor['state'] == MachineState::Offline) {
			Yii::log("executor[{$this->hostname}] is offline", CLogger::LEVEL_INFO, EXECUTOR_LOG);
			return false;
		}
		return true;
	}
	
	/**
	 * Get ready tasks
	 */
	public function getReadyTasks() {
		return $this->taskService->getReadyTasks($this->executor['executor_id']);	
	}
	
	/**
	 * Start task worker
	 */
	public function execute($task) {
		Yii::log("begin start worker of task [{$task['task_id']}]", CLogger::LEVEL_INFO, EXECUTOR_LOG);		
		if($this->isBusy()) {
			Yii::log("executor[{$this->hostname}] is busy", CLogger::LEVEL_INFO, EXECUTOR_LOG);
			if(strpos($this->task['detail'], 'busy') === false) {
				$this->taskService->editTaskAttributes($task['task_id'], 
					array('detail'	=>	"executor[{$this->hostname}] is busy, waiting..."));
			}
			return true;
		}
		
		if(!$this->addCurrentWorkers()) {
			Yii::log("add current workers count failed", CLogger::LEVEL_ERROR, EXECUTOR_LOG);
			return false;
		}

		$this->taskService->editTaskAttributes($task['task_id'], array(
			'state'		=>	TaskState::Running,
			'start_time'=>	date('Y-m-d H:i:s'))
		);
		Yii::log("task [{$task['task_id']}] state change to running", CLogger::LEVEL_INFO, EXECUTOR_LOG);
		if($this->startWorker($task)) {
			Yii::log("start worker of task[{$task['task_id']}] successfully", CLogger::LEVEL_INFO, EXECUTOR_LOG);
		}
		else {
			Yii::log("start worker of task[{$task['task_id']}] failed", CLogger::LEVEL_ERROR, EXECUTOR_LOG);
		}
	}
	
	private function isBusy() {
		$executor = $this->schedulerService->getExecutorMachineById($this->executor['executor_id']);
		return intval($executor['current_workers']) >= intval($executor['max_workers']);
	}
	
	private function addCurrentWorkers() {
		return $this->schedulerService->updateCurrentCount($this->executor['executor_id']);
	}
	
	private function startWorker($task) {		
		$runtimePath = PathUtil::getRuntimePath($task['task_id'], $task['job_id'], $task['date']);
		if (!file_exists($runtimePath) && mkdir($runtimePath, 0755, true) === false) {
			$this->taskService->editTaskAttributes($task['task_id'],
				array('detail'	=>	'make runtime dir failed, maybe disk is full')
			);
			Yii::log('make runtime dir failed, maybe disk is full', CLogger::LEVEL_ERROR, EXECUTOR_LOG);
			return false;
		}
		/*
		$jsonRuntime = $task['runtime'];
		$bytesWritten = file_put_contents("$runtimePath/taskinfo", $jsonRuntime);
		if($bytesWritten === false || $bytesWritten !== strlen($jsonRuntime))  {
			$this->taskService->editTaskAttributes($task['task_id'],
				array('detail'  =>  'write taskinfo failed, maybe disk is full')
			);
			Yii::log('write taskinfo failed, maybe disk is full', CLogger::LEVEL_ERROR,
				EXECUTOR_LOG);
			return false;
		}
		*/
		$argv = $this->combinArgs($task);

		$executor_log_name = $runtimePath . ".log";
		$cmd = 'nohup '. PHP_EXEC . ' ' . WORKER_PHP . " \"$argv\">>"
		. $executor_log_name . ' 2>&1 </dev/null &';

		Yii::log("start task worker: $cmd", CLogger::LEVEL_INFO, EXECUTOR_LOG);
		exec($cmd);
		return true;
	}
	
	private function combinArgs($task) {
		$hadoopPriority = Priority::$names[$task['priority']];
		return 'worker/'.$task['job_id'].'/'.$task['task_id']
			 . '/' . $task['task_type'] . '/' . $task['date'] 
			 . '/' . $task['frequency'] . '/' . $task['workflow_name']
			 . '/' . $task['executor_id'] . '/' . $hadoopPriority;
	}
}
